<!DOCTYPE html>
<html>

<head>
  <title>canvas test-31 - 高级loading动画</title>
  <style type="text/css">
  * {
    margin: 0;
    padding: 0
  }
  
  body {
    background: #4c4d55;
  }
  
  pre {
    padding: 10px 0
  }
  </style>
</head>

<body>
  <pre></pre>
  <p></p>
  <canvas id="stage" width="400" height="400" style="border: 0px solid red; margin: 20px auto; display: block"></canvas>
  <script src="raf.js"></script>
  <script src="vector2.js"></script>
  <script src="resource.js"></script>
  <script type="text/javascript">
  var canvas = document.getElementById('stage');
  var ctx = canvas.getContext('2d');

  var canvasWidth = canvas.width;
  var canvasHeight = canvas.height;
  var centerX = canvasWidth / 2;
  var centerY = canvasHeight / 2;
  var bbox = canvas.getBoundingClientRect();
  var ARC = Math.PI / 180;
  var ARC1 = ARC * 90;
  var ARC2 = ARC * 180;
  var ARC3 = ARC * 270;
  var ARC4 = ARC * 360;
  var DEG = 180 / Math.PI;
  var radius = 100;
  var ended = false;

  // 点
  var spot = {
    pos: 0,
    angle: 0,
    radius: 4,
    round: 0,
    angleSwing: 360,
    posSwing: radius,
    startTime: 0,
    visible: true,
    angleDuration: 1200,
    failDuration: 400,
    angleMode: true,
    update: function(now) {
      if (this.visible) {
        this.angleMode = this.round !== 2;
        var ts = now - this.startTime;
        var duration = this.angleMode ? this.angleDuration : this.failDuration;
        var percent = Math.min(1, ts / duration);
        if (this.angleMode) {
          this.angle = this.angleSwing * percent;
        } else {
          this.pos = (1 - percent) * this.posSwing;
        }
        if (percent === 1) {
          if (this.angleMode) {
            this.round += 1;
            this.angle = 0;
          }
          this.startTime = now;
          this.visible = this.angleMode;
        }
      }
    }
  };
  // 弧
  var arc = {
    angle: 0,
    size: 20,
    round: 0,
    startSize: 20,
    radius: radius,
    angleSwing: 360,
    sizeSwing: 170,
    angleDuration: 1200,
    startTime: 0,
    visible: true,
    update: function(now) {
      if (this.visible) {
        var ts = now - this.startTime;
        var percent = Math.min(1, ts / this.angleDuration);
        this.angle = this.angleSwing * sinusoidalCurve(percent);
        this.size = this.startSize + this.sizeSwing * percent;
        if (percent === 1) {
          this.round += 1;
          this.startTime = now;
          this.startSize += this.sizeSwing;
          this.visible = this.round !== 2;
        }
      }
    }
  };
  // clip
  var clipped = {
    radius: 5,
    startRadius: 5,
    radiusSwing: radius,
    duration: 800,
    startTime: 0,
    ended: false,
    update: function(now) {
      var ts = now - this.startTime;
      var percent = Math.min(1, ts / this.duration);
      this.radius = this.startRadius + sinusoidalCurve(percent) * this.radiusSwing;
      if (percent === 1) {
        this.ended = true;
      }
    }
  };

  window.onload = function() {
    rendering();
    arc.startTime = spot.startTime = Date.now();
    window.requestAnimationFrame(redraw);
  };

  function redraw() {
    ctx.clearRect(0, 0, canvasWidth + 1, canvasHeight + 1);
    updating();
    rendering();
    if (!ended) {
      window.requestAnimationFrame(redraw);
    }
  }

  function updating() {
    var now = Date.now();
    spot.update(now);
    arc.update(now);
    if (!arc.visible && !spot.visible && !clipped.startTime) {
      clipped.startTime = now;
    }
    if (clipped.startTime) {
      clipped.update(now);
    }
    ended = !arc.visible && !spot.visible && clipped.ended;
  }

  function rendering() {
    drawBackground();
    drawArc();
    drawSpot();
    drawClipped();
  }

  function drawBackground() {
    if (!arc.visible && !spot.visible) {
      return;
    }
    var size = 56;
    var text = 'LOGO';
    ctx.save();
    ctx.strokeStyle = 'red';
    ctx.fillStyle = 'red';
    ctx.lineWidth = 8;
    ctx.font = 'bold ' + size + 'px Arial';
    ctx.beginPath();
    ctx.arc(centerX, centerY, radius, 0, ARC4);
    ctx.closePath();
    ctx.stroke();
    var offset = ctx.measureText(text).width / 2;
    ctx.fillText(text, centerX - offset, centerY + size * .4);
    ctx.restore();
  }

  function drawClipped() {
    if (arc.visible || spot.visible) {
      return;
    }
    var size = 56;
    var text = 'LOGO';
    ctx.save();
    ctx.strokeStyle = 'white';
    ctx.fillStyle = 'white';
    ctx.lineWidth = 8;
    ctx.font = 'bold ' + size + 'px Arial';
    ctx.beginPath();
    ctx.arc(centerX, centerY, clipped.radius, 0, ARC4);
    ctx.closePath();
    ctx.clip();
    ctx.beginPath();
    ctx.arc(centerX, centerY, radius, 0, ARC4);
    ctx.closePath();
    ctx.stroke();
    var offset = ctx.measureText(text).width / 2;
    ctx.fillText(text, centerX - offset, centerY + size * .4);
    ctx.restore();
  }

  function drawArc() {
    if (!arc.visible) {
      return;
    }
    var arcStart = (arc.angle - 90) * ARC;
    var arcEnd = (arc.angle + arc.size - 90) * ARC;
    ctx.save();
    ctx.strokeStyle = 'white';
    ctx.lineWidth = 8;
    ctx.beginPath();
    ctx.arc(centerX, centerY, radius, arcStart, arcEnd);
    ctx.stroke();
    ctx.restore();
  }

  function drawSpot() {
    if (!spot.visible) {
      return;
    }
    var a = (spot.angle - 90) * ARC;
    var ax = spot.angleMode ? centerX + radius * Math.cos(a) : centerX;
    var ay = spot.angleMode ? centerY + radius * Math.sin(a) : centerY - spot.pos;
    ctx.save();
    ctx.fillStyle = 'white';
    ctx.beginPath();
    ctx.arc(ax, ay, spot.radius, 0, ARC4);
    ctx.closePath();
    ctx.fill();
    ctx.restore();
  }

  function sinusoidalCurve(k) {
    return 0.5 * (1 - Math.cos(Math.PI * k));
  }

  function circularCurve(k) {
    if ((k *= 2) < 1) {
      return -0.5 * (Math.sqrt(1 - k * k) - 1);
    }
    return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
  }
  </script>
</body>

</html>
